﻿using System;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using Jacobi.Seesmic.FeedReader.Model;
using Jacobi.Seesmic.FeedReader.PluginSettingsView;
using Seesmic.Sdp.Extensibility;

namespace Jacobi.Seesmic.FeedReader
{
    /// <summary>
    /// The Plugin object implement the root object for the Seesmic plugin.
    /// </summary>
    /// <remarks>
    /// It manages the global collection <see cref="FeedInfo"/> items
    /// as well as committing and reverting the <see cref="FeedInfo"/>s for editing.
    /// </remarks>
    [Export(typeof(IPlugin))]
    public sealed class Plugin : IPlugin
    {
        /// <summary>
        /// Unique id for this plugin. See also Properties/AppManifest.xml.
        /// </summary>
        internal static readonly Guid UniqueId = new Guid("C508D9D5-C703-4149-8EB4-C6BDFB356383");
        /// <summary>The key that is used to store the <see cref="FeedInfo"/> collection.</summary>
        private const string SettingsStorageKey = "FeedInfos";

        /// <summary>
        /// A collection of <see cref="FeedInfo"/> instances that are being edited in the settings view.
        /// </summary>
        /// <remarks>Property is null when no editing is in progress.</remarks>
        internal static FeedInfoCollection EditFeedInfos { get; private set; }

        /// <summary>
        /// The collection of <see cref="FeedInfo"/> instances that are being shown on the <see cref="FeedTimeline"/>.
        /// </summary>
        internal static FeedInfoCollection FeedInfos { get; private set; }

        /// <summary>
        /// Gets or sets an implementation of <see cref="IStorageService"/> provided by that host.
        /// </summary>
        /// <remarks>Injected by MEF.</remarks>
        [Import(typeof(IStorageService))]
        public IStorageService StorageService { get; set; }

        /// <summary>
        /// Gets the unique Id for this plugin.
        /// </summary>
        public Guid Id
        {
            get { return UniqueId; }
        }

        /// <summary>
        /// Called by the host when the plugin is loaded. Initializes the <see cref="FeedInfos"/> collection.
        /// </summary>
        public void Initialize()
        {
#if DEBUG
            MessageBox.Show("Feed Reader: Attach the debugger now!");
#endif

            FeedInfos = StorageService.GetValue<FeedInfoCollection>(UniqueId, SettingsStorageKey);

            if (FeedInfos == null)
            {
                FeedInfos = new FeedInfoCollection();
            }
        }

        /// <summary>
        /// Called by the host when the user presses the Save button on the settings dialog.
        /// </summary>
        /// <remarks>
        /// All <see cref="FeedInfos"/> are persisted and refreshed.
        /// </remarks>
        public void CommitSettings()
        {
            if (EditFeedInfos != null)
            {
                StorageService.SetValue<FeedInfoCollection>(UniqueId, SettingsStorageKey, EditFeedInfos);

                MergeFeedInfos(EditFeedInfos);

                EditFeedInfos = null;
            }
        }

        /// <summary>
        /// Called by the host when the user presses the Cancel button in the settings dialog.
        /// </summary>
        /// <remarks>
        /// We simply cancel the <see cref="EditFeedInfos"/> that were used for editing.
        /// Nothing is persisted or refreshed.
        /// </remarks>
        public void RevertSettings()
        {
            EditFeedInfos = null;
        }

        private static void MergeFeedInfos(FeedInfoCollection feedInfos)
        {
            var currentInfos = new FeedInfoCollection();
            currentInfos.AddRange(FeedInfos);

            // process edits on existing items
            foreach (var targetItem in currentInfos)
            {
                if (feedInfos.ContainsKey(targetItem))
                {
                    var sourceItem = feedInfos[targetItem.Url];
                    targetItem.Assign(sourceItem);
                }
            }

            // retrieve additions
            var added = from feedInfo in feedInfos
                        where !currentInfos.ContainsKey(feedInfo)
                        select feedInfo;

            FeedInfos.AddRange(added);

            // retrieve removals
            var removed = from feedInfo in currentInfos
                          where !feedInfos.ContainsKey(feedInfo)
                          select feedInfo;

            foreach (var removedItem in removed)
            {
                FeedInfos.Remove(removedItem);
            }
        }

        private DataTemplate _settingsView;

        /// <summary>
        /// Gets the default settings view data template.
        /// </summary>
        public DataTemplate SettingsTemplate
        {
            get
            {
                if (_settingsView == null)
                {
                    _settingsView = PluginSettingsViews.DefaultView;
                }

                return _settingsView;
            }
        }

        /// <summary>
        /// Gets the data binding object that goes with the <see cref="SettingsTemplate"/>.
        /// </summary>
        /// <remarks>
        /// By default the <see cref="SettingsTemplate"/> has this Plugin instance as its DataContext.
        /// Our Settings view template rebinds to this property.
        /// Note that the <see cref="EditFeedInfos"/> is populated and the settings view is bound to that (not <see cref="FeedInfos"/>).
        /// </remarks>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
            Justification = "Need an instance property for DataBinding to the settings view.")]
        public PluginSettingsViewModel SettingsViewModel
        {
            get
            {
                EditFeedInfos = FeedInfos.DeepCopy();

                return new PluginSettingsViewModel(EditFeedInfos);
            }
        }
    }
}